package service;

import static org.junit.Assert.*;

import java.util.GregorianCalendar;

import model.DailyIrregular;
import model.DailyRegular;
import model.Drug;
import model.PN;
import model.Patient;

import org.junit.Before;
import org.junit.Test;

/**
 * Test the Service class
 */
public class ServiceTest {
	private Service service;
	private Patient patient;
	private Drug drug;
	private DailyIrregular dailyIrregular;
	private DailyRegular dailyRegular;
	private PN pn;

	/**
	 * Initialize common variables used in the following tests.
	 */
	@Before
	public void setUp() throws Exception {
		service = Service.getInstance();
		patient = new Patient("8181", "Asta", 62);
		drug = new Drug("valium", 0.3, 5.0, 12, "ml");
	}

	/**
	 * Test if the getInstance method returns the correct object with the correct type.
	 */
	@Test
	public void testGetInstance() {
		// Check if getInstance returns an object of type Service.
		assertTrue(Service.getInstance() instanceof Service);
		
		// Check if the service variable is the same object as returned by getInstance.
		assertSame(service, Service.getInstance());
	}

	/**
	 * Test to see if giving dates in incorrect order results with a RuntimeException.
	 */
	@Test(expected= RuntimeException.class)
	public void testCreatePNPrescriptionException() {
		// First date is after the second date, which should return an error.
		pn = service.createPNPrescription(new GregorianCalendar(2008, 3, 1),
				new GregorianCalendar(2008, 2, 28), patient, drug, 0);
	}
	
	/**
	 * Test the createPNPrescription method with different dates.
	 */
	@Test
	public void testCreatePNPrescription() {
		// One day
		pn = service.createPNPrescription(new GregorianCalendar(2008, 3, 1),
				new GregorianCalendar(2008, 3, 1), patient, drug, 0);
		assertEquals(1, pn.dayCount());
		
		// Short time, ten days.
		pn = service.createPNPrescription(new GregorianCalendar(2008, 3, 1),
				new GregorianCalendar(2008, 3, 10), patient, drug, 0);
		assertEquals(10, pn.dayCount());
		
		// Long time, one year.
		pn = service.createPNPrescription(new GregorianCalendar(2008, 3, 1),
				new GregorianCalendar(2009, 3, 1), patient, drug, 0);
		assertEquals(366, pn.dayCount());
		
		// Leap year 
		pn = service.createPNPrescription(new GregorianCalendar(2008, 1, 28),
				new GregorianCalendar(2008, 2, 1), patient, drug, 0);
		assertEquals(3, pn.dayCount());
	}

	/**
	 * Test to see if giving dates in incorrect order results with a RuntimeException.
	 */
	@Test(expected = RuntimeException.class)
	public void testCreateDailyRegularPrescriptionException() {
		// First date is after the second date, which should return an error.
		dailyRegular = service.createDailyRegularPrescription(
				new GregorianCalendar(2008, 3, 1), new GregorianCalendar(2008,
						2, 28), patient, drug, 0, 0, 0, 0);
	}
	
	@Test
	public void testCreateDailyRegularPrescription() {
		// One day
		dailyRegular = service.createDailyRegularPrescription(
				new GregorianCalendar(2008, 3, 1), new GregorianCalendar(2008,
						3, 1), patient, drug, 0, 0, 0, 0);
		assertEquals(1, dailyRegular.dayCount());

		// Short time, ten days.
		dailyRegular = service.createDailyRegularPrescription(
				new GregorianCalendar(2008, 3, 1), new GregorianCalendar(2008,
						3, 10), patient, drug, 0, 0, 0, 0);
		assertEquals(10, dailyRegular.dayCount());

		// Long time, one year.
		dailyRegular = service.createDailyRegularPrescription(
				new GregorianCalendar(2008, 3, 1), new GregorianCalendar(2009,
						3, 1), patient, drug, 0, 0, 0, 0);
		assertEquals(366, dailyRegular.dayCount());

		// Leap year 
		dailyRegular = service.createDailyRegularPrescription(
				new GregorianCalendar(2008, 1, 28), new GregorianCalendar(2008,
						2, 1), patient, drug, 0, 0, 0, 0);
		assertEquals(3, dailyRegular.dayCount());

	}

	/**
	 * Test to see if giving dates in incorrect order results with a RuntimeException.
	 */
	@Test(expected = RuntimeException.class)
	public void testCreateDailyIrregularPrescriptionException() {
		// First date is after the second date, which should return an error.
		dailyIrregular = service.createDailyIrregularPrescription(
				new GregorianCalendar(2008, 3, 1), new GregorianCalendar(2008,
						2, 28), patient, drug, 
						new String[] {"9:00", "14:00", "16:00"}, 
						new double[] {1, 2, 3});
	}
	
	@Test
	public void testCreateDailyIrregularPrescription() {
		// One day
		dailyIrregular = service.createDailyIrregularPrescription(
				new GregorianCalendar(2008, 3, 1), new GregorianCalendar(2008,
						3, 1), patient, drug,
						new String[] {"9:00", "14:00", "16:00"}, 
						new double[] {1, 2, 3});
		assertEquals(1, dailyIrregular.dayCount());

		// Short time, ten days.
		dailyIrregular = service.createDailyIrregularPrescription(
				new GregorianCalendar(2008, 3, 1), new GregorianCalendar(2008,
						3, 10), patient, drug,
						new String[] {"9:00", "14:00", "16:00"}, 
						new double[] {1, 2, 3});
		assertEquals(10, dailyIrregular.dayCount());

		// Long time, one year.
		dailyIrregular = service.createDailyIrregularPrescription(
				new GregorianCalendar(2008, 3, 1), new GregorianCalendar(2009,
						3, 1), patient, drug,
						new String[] {"9:00", "14:00", "16:00"}, 
						new double[] {1, 2, 3});
		assertEquals(366, dailyIrregular.dayCount());

		// Leap year 
		dailyIrregular = service.createDailyIrregularPrescription(
				new GregorianCalendar(2008, 1, 28), new GregorianCalendar(2008,
						2, 1), patient, drug,
						new String[] {"9:00", "14:00", "16:00"}, 
						new double[] {1, 2, 3});
		assertEquals(3, dailyIrregular.dayCount());
	}

	/**
	 * Test to see if giving dates in incorrect order results with a RuntimeException.
	 */
	@Test(expected = RuntimeException.class)
	public void prescriptionPNUsedException() {
		pn = service.createPNPrescription(new GregorianCalendar(2008, 3, 2),
				new GregorianCalendar(2008, 3, 1), patient, drug, 0);
	}

	/**
	 * Test the prescriptionPNUsed method which records usage of prescriptions.
	 */
	@Test
	public void testPrescriptionPNUsed() {
		// Create a PN prescription
		pn = service.createPNPrescription(new GregorianCalendar(2008, 3, 1),
				new GregorianCalendar(2008, 3, 1), patient, drug, 0);
		
		// Use that prescription on the same date.
		service.prescriptionPNUsed(pn, new GregorianCalendar(2008, 3, 1));
		
		// Check if the prescription has been used on that date which should be true.
		assertTrue(pn.doseGiven(new GregorianCalendar(2008, 3, 1)));

		// Check if the prescription has been used on another date, which should be false.
		assertFalse(pn.doseGiven(new GregorianCalendar(2008, 3, 2)));
	}

	/**
	 * Test the recommendedDosePrDay method which calculates 
	 * recommended dose per day based on the patient's weight. 
	 */
	@Test
	public void testRecommendedDosePrDay() {
		// Create a drug for the test with specific values.
		drug = new Drug(" testdrug", 1, 2, 4, "kg");

		// Small patient.
		patient = new Patient("1234567890", "joe", 10);
		assertEquals(10, service.recommendedDosePrDay(patient, drug));

		// Small patient, midway to the high limit.
		patient = new Patient("1234567890", "joe", 20);
		assertEquals(20, service.recommendedDosePrDay(patient, drug));

		// Small patient, on the high limit.
		patient = new Patient("1234567890", "joe", 24);
		assertEquals(24, service.recommendedDosePrDay(patient, drug));

		// Medium patient, on the lower limit.
		patient = new Patient("1234567890", "joe", 25);
		assertEquals(50, service.recommendedDosePrDay(patient, drug));

		// Medium patient, close to the lower limit,
		patient = new Patient("1234567890", "joe", 26);
		assertEquals(52, service.recommendedDosePrDay(patient, drug));

		// Medium patient, midway to the high limit.
		patient = new Patient("1234567890", "joe", 50);
		assertEquals(100, service.recommendedDosePrDay(patient, drug));

		// Medium patient, close to the high limit.
		patient = new Patient("1234567890", "joe", 119);
		assertEquals(238, service.recommendedDosePrDay(patient, drug));

		// Medium patient, on the high limit.
		patient = new Patient("1234567890", "joe", 120);
		assertEquals(240, service.recommendedDosePrDay(patient, drug));

		// Big patient, on the low limit.
		patient = new Patient("1234567890", "joe", 121);
		assertEquals(484, service.recommendedDosePrDay(patient, drug));

		// Big patient, somewhat bigger.
		patient = new Patient("1234567890", "joe", 150);
		assertEquals(600, service.recommendedDosePrDay(patient, drug));

		// Big patient, even bigger.
		patient = new Patient("1234567890", "joe", 210);
		assertEquals(840, service.recommendedDosePrDay(patient, drug));

		// Initialize the drug with different values from before.
		drug = new Drug(" testdrug", 8, 16, 32, "kg");

		// Small patient.
		patient = new Patient("1234567890", "joe", 10);
		assertEquals(80, service.recommendedDosePrDay(patient, drug));

		// Small patient, midway to the high limit.
		patient = new Patient("1234567890", "joe", 20);
		assertEquals(160, service.recommendedDosePrDay(patient, drug));

		// Small patient, on the high limit.
		patient = new Patient("1234567890", "joe", 24);
		assertEquals(192, service.recommendedDosePrDay(patient, drug));

		// Medium patient, on the lower limit.
		patient = new Patient("1234567890", "joe", 25);
		assertEquals(400, service.recommendedDosePrDay(patient, drug));

		// Medium patient, close to the lower limit,
		patient = new Patient("1234567890", "joe", 26);
		assertEquals(416, service.recommendedDosePrDay(patient, drug));

		// Medium patient, midway to the high limit.
		patient = new Patient("1234567890", "joe", 50);
		assertEquals(800, service.recommendedDosePrDay(patient, drug));

		// Medium patient, close to the high limit.
		patient = new Patient("1234567890", "joe", 119);
		assertEquals(1904, service.recommendedDosePrDay(patient, drug));

		// Medium patient, on the high limit.
		patient = new Patient("1234567890", "joe", 120);
		assertEquals(1920, service.recommendedDosePrDay(patient, drug));

		// Big patient, on the low limit.
		patient = new Patient("1234567890", "joe", 121);
		assertEquals(3872, service.recommendedDosePrDay(patient, drug));

		// Big patient, somewhat bigger.
		patient = new Patient("1234567890", "joe", 150);
		assertEquals(4800, service.recommendedDosePrDay(patient, drug));

		// Big patient, even bigger.
		patient = new Patient("1234567890", "joe", 210);
		assertEquals(6720, service.recommendedDosePrDay(patient, drug));
	}

	//TODO: !!!!! NEED TO ADD THIS TO THE DOCUMENTATION !!!!! 
	
	/**
	 * Check the checkDates method, which checks if the 
	 * two dates are in the correct order or the same.
	 */
	@Test
	public void testCheckDates() {
		GregorianCalendar firstDate;
		GregorianCalendar secondDate;
		
		// firstDate < secondDate, one day.
		firstDate = new GregorianCalendar(2008, 3, 1);
		secondDate = new GregorianCalendar(2008, 3, 2);
		assertTrue(service.checkDates(firstDate, secondDate));
		
		// firstDate < secondDate, one month.
		firstDate = new GregorianCalendar(2008, 3, 1);
		secondDate = new GregorianCalendar(2008, 4, 2);
		assertTrue(service.checkDates(firstDate, secondDate));
		
		// firstDate < secondDate, one year.
		firstDate = new GregorianCalendar(2008, 3, 1);
		secondDate = new GregorianCalendar(2009, 3, 2);
		assertTrue(service.checkDates(firstDate, secondDate));
		
		// firstDate > secondDate, one day - should return false.
		firstDate = new GregorianCalendar(2008, 3, 2);
		secondDate = new GregorianCalendar(2008, 3, 1);
		assertFalse(service.checkDates(firstDate, secondDate));
		
		// firstDate > secondDate, one month - should return false.
		firstDate = new GregorianCalendar(2008, 4, 2);
		secondDate = new GregorianCalendar(2008, 3, 1);
		assertFalse(service.checkDates(firstDate, secondDate));
		
		// firstDate > secondDate, one year - should return false.
		firstDate = new GregorianCalendar(2009, 3, 2);
		secondDate = new GregorianCalendar(2008, 3, 1);
		assertFalse(service.checkDates(firstDate, secondDate));
	}
}
